package com.ycuwq.datepicker;

import com.ycuwq.datepicker.util.AttrUtils;
import com.ycuwq.datepicker.util.LinearGradient;
import com.ycuwq.datepicker.util.LogUtil;
import com.ycuwq.datepicker.util.Utils;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.VelocityDetector;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.agp.utils.RectFloat;
import ohos.agp.utils.TextAlignment;
import ohos.app.Context;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.multimodalinput.event.TouchEvent;

import java.text.Format;
import java.util.List;


/**
 * 滚动选择器
 * Created by ycuwq on 2017/12/12.
 *
 * @param <T> parameter
 */
@SuppressWarnings({"FieldCanBeLocal", "unused", "SameParameterValue"})
public class WheelPicker<T> extends Component {
    private static final String TAG = "WheelPicker";
    /**
     * * The constant WheelPicker_itemTextSize
     */
    public static final String WHEELPICKER_ITEMTEXTSIZE = "itemTextSize";
    /**
     * * The constant WheelPicker_halfVisibleItemCount
     */
    public static final String WHEELPICKER_HALFVISIBLEITEMCOUNT = "halfVisibleItemCount";
    /**
     * * The constant WheelPicker_itemTextColor
     */
    public static final String WHEELPICKER_ITEMTEXTCOLOR = "itemTextColor";
    /**
     * * The constant WheelPicker_textGradual
     */
    public static final String WHEELPICKER_TEXTGRADUAL = "textGradual";
    /**
     * * The constant WheelPicker_selectedTextColor
     */
    public static final String WHEELPICKER_SELECTEDTEXTCOLOR = "selectedTextColor";
    /**
     * * The constant WheelPicker_selectedTextSize
     */
    public static final String WHEELPICKER_SELECTEDTEXTSIZE = "selectedTextSize";
    /**
     * * The constant WheelPicker_itemWidthSpace
     */
    public static final String WHEELPICKER_ITEMWIDTHSPACE = "itemWidthSpace";
    /**
     * * The constant WheelPicker_itemHeightSpace
     */
    public static final String WHEELPICKER_ITEMHEIGHTSPACE = "itemHeightSpace";
    /**
     * * The constant WheelPicker_zoomInSelectedItem
     */
    public static final String WHEELPICKER_ZOOMINSELECTEDITEM = "zoomInSelectedItem";
    /**
     * * The constant WheelPicker_wheelCyclic
     */
    public static final String WHEELPICKER_WHEELCYCLIC = "wheelCyclic";
    /**
     * * The constant WheelPicker_wheelCurtain
     */
    public static final String WHEELPICKER_WHEELCURTAIN = "wheelCurtain";
    /**
     * * The constant WheelPicker_wheelCurtainColor
     */
    public static final String WHEELPICKER_WHEELCURTAINCOLOR = "wheelCurtainColor";
    /**
     * * The constant WheelPicker_wheelCurtainBorder
     */
    public static final String WHEELPICKER_WHEELCURTAINBORDER = "wheelCurtainBorder";
    /**
     * * The constant WheelPicker_wheelCurtainBorderColor
     */
    public static final String WHEELPICKER_WHEELCURTAINBORDERCOLOR = "wheelCurtainBorderColor";
    /**
     * * The constant WheelPicker_indicatorTextColor
     */
    public static final String WHEELPICKER_INDICATORTEXTCOLOR = "indicatorTextColor";
    /**
     * * The constant WheelPicker_itemMaximumWidthText
     */
    public static final String WHEELPICKER_ITEMMAXIMUMWIDTHTEXT = "itemMaximumWidthText";
    /**
     * * The constant WheelPicker_currentItemPosition
     */
    public static final String WHEELPICKER_CURRENTITEMPOSITION = "currentItemPosition";
    /**
     * * The constant WheelPicker_indicatorText
     */
    public static final String WHEELPICKER_INDICATORTEXT = "indicatorText";
    /**
     * * The constant WheelPicker_indicatorTextSize
     */
    public static final String WHEELPICKER_INDICATORTEXTSIZE = "indicatorTextSize";
    /**
     * * The constant WheelItemTextSize
     */
    public static int WheelItemTextSize = 16;
    /**
     * * The constant com_ycuwq_datepicker_selectedTextColor
     */
    public static int com_ycuwq_datepicker_selectedTextColor = 0xff33aaff;
    /**
     * * The constant WheelSelectedItemTextSize
     */
    public static int WheelSelectedItemTextSize = 20;
    /**
     * * The constant WheelItemWidthSpace
     */
    public static int WheelItemWidthSpace = 32;
    /**
     * * The constant WheelItemHeightSpace
     */
    public static int WheelItemHeightSpace = 16;
    /**
     * * The constant com_ycuwq_datepicker_divider
     */
    public static int com_ycuwq_datepicker_divider = 0xffcccccc;
    /**
     * 数据集合
     */
    private List<T> mDataList;
    /**
     * The constant M data format
     */
    private Format mDataFormat;
    /**
     * Item的Text的颜色
     */
    private int mTextColor;
    /**
     * The constant M text size
     */
    private int mTextSize;
    /**
     * The constant M text paint
     */
    private Paint mTextPaint;
    /**
     * 字体渐变，开启后越靠近边缘，字体越模糊
     */
    private boolean mIsTextGradual;
    /**
     * 选中的Item的Text颜色
     */
    private int mSelectedItemTextColor;
    /**
     * 选中的Item的Text大小
     */
    private int mSelectedItemTextSize;
    /**
     * The constant M selected item paint
     */
    private Paint mSelectedItemPaint;
    /**
     * 指示器文字
     * 会在中心文字后边多绘制一个文字。
     */
    private String mIndicatorText;
    /**
     * 指示器文字颜色
     */
    private int mIndicatorTextColor;
    /**
     * 指示器文字大小
     */
    private int mIndicatorTextSize;
    /**
     * The constant M indicator paint
     */
    private Paint mIndicatorPaint;
    /**
     * The constant M paint
     */
    private Paint mPaint;
    /**
     * 最大的一个Item的文本的宽高
     */
    private int mTextMaxWidth;
    /**
     * M text max height
     */
    private int mTextMaxHeight;
    /**
     * 输入的一段文字，可以用来测量 mTextMaxWidth
     */
    private String mItemMaximumWidthText;
    /**
     * 显示的Item一半的数量（中心Item上下两边分别的数量）
     * 总显示的数量为 mHalfVisibleItemCount * 2 + 1
     */
    private int mHalfVisibleItemCount;
    /**
     * 两个Item之间的高度间隔
     */
    private int mItemHeightSpace;
    /**
     * M item width space
     */
    private int mItemWidthSpace;
    /**
     * The constant M item height
     */
    private int mItemHeight;
    /**
     * 当前的Item的位置
     */
    private int mCurrentPosition;
    /**
     * 是否将中间的Item放大
     */
    private boolean mIsZoomInSelectedItem;
    /**
     * 是否显示幕布，中央Item会遮盖一个颜色颜色
     */
    private boolean mIsShowCurtain;
    /**
     * 幕布颜色
     */
    private int mCurtainColor;
    /**
     * 是否显示幕布的边框
     */
    private boolean mIsShowCurtainBorder;
    /**
     * 幕布边框的颜色
     */
    private int mCurtainBorderColor;
    /**
     * 整个控件的可绘制面积
     */
    private RectFloat mDrawnRect;
    /**
     * 中心被选中的Item的坐标矩形
     */
    private RectFloat mSelectedItemRect;
    /**
     * 第一个Item的绘制Text的坐标
     */
    private int mFirstItemDrawX;
    /**
     * M first item draw y
     */
    private int mFirstItemDrawY;
    /**
     * 中心的Item绘制text的Y轴坐标
     */
    private int mCenterItemDrawnY;
    /**
     * The constant M scroller
     */
    private Scroller mScroller;
    /**
     * The constant M touch slop
     */
    private int mTouchSlop;
    /**
     * 该标记的作用是，令mTouchSlop仅在一个滑动过程中生效一次。
     */
    private boolean mTouchSlopFlag;
    /**
     * The constant M tracker
     */
    private VelocityDetector mTracker;
    /**
     * The constant M touch down y
     */
    private int mTouchDownY;
    /**
     * Y轴Scroll滚动的位移
     */
    private int mScrollOffsetY;
    /**
     * 最后手指Down事件的Y轴坐标，用于计算拖动距离
     */
    private int mLastDownY;
    /**
     * 是否循环读取
     */
    private boolean mIsCyclic = true;
    /**
     * 最大可以Fling的距离
     */
    private int mMaxFlingY;
    /**
     * M min fling y
     */
    private int mMinFlingY;
    /**
     * 滚轮滑动时的最小/最大速度
     */
    private int mMinimumVelocity = 50;
    /**
     * M maximum velocity
     */
    private int mMaximumVelocity = 12000;
    /**
     * 是否是手动停止滚动
     */
    private boolean mIsAbortScroller;
    /**
     * Upgrade SDK 5 to adapt to the onRefresh and onDraw sequences.
     */
    private boolean mIsOnRefreshed = false;
    /**
     * The constant M linear gradient
     */
    private LinearGradient mLinearGradient;
    /**
     * The constant M handler
     */
    private EventHandler mHandler = new EventHandler(EventRunner.getMainEventRunner());
    /**
     * The constant M on wheel change listener
     */
    private OnWheelChangeListener<T> mOnWheelChangeListener;
    /**
     * The constant M scroller runnable
     */
    private Runnable mScrollerRunnable = new Runnable() {
        @Override
        public void run() {
            if (mScroller.computeScrollOffset()) {
                mScrollOffsetY = mScroller.getCurrY();
                LogUtil.info(TAG, "run: " + mScrollOffsetY);
                invalidate();
                mHandler.postTask(this, 16);
            }
            if (mScroller.isFinished() || (mScroller.getFinalY() == mScroller.getCurrY()
                    && mScroller.getFinalX() == mScroller.getCurrX())) {
                if (mItemHeight == 0) {
                    return;
                }
                int position = -mScrollOffsetY / mItemHeight;
                position = fixItemPosition(position);
                if (mCurrentPosition != position) {
                    mCurrentPosition = position;
                    if (mOnWheelChangeListener == null) {
                        return;
                    }
                    mOnWheelChangeListener.onWheelSelected(mDataList.get(position),
                            position);
                }
            }
        }
    };

    /**
     * Wheel picker
     *
     * @param context context
     */
    public WheelPicker(Context context) {
        this(context, null);
    }

    /**
     * Wheel picker
     *
     * @param context context
     * @param attrs   attrs
     */
    public WheelPicker(Context context, AttrSet attrs) {
        this(context, attrs, null);
    }

    /**
     * Wheel picker
     *
     * @param context      context
     * @param attrs        attrs
     * @param defStyleAttr def style attr
     */
    public WheelPicker(Context context, AttrSet attrs, String defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initAttrs(context, attrs);
        initPaint();
        mLinearGradient = new LinearGradient(mTextColor, mSelectedItemTextColor);
        mDrawnRect = new RectFloat();
        mSelectedItemRect = new RectFloat();
        mScroller = new Scroller(context);
        mTouchSlop = 8;
        setLayoutRefreshedListener(this::onRefreshed);
        addDrawTask(this::onDraw);
        setTouchEventListener(this::onTouchEvent);
    }

    /**
     * Init attrs *
     *
     * @param context context
     * @param attrs   attrs
     */
    private void initAttrs(Context context, AttrSet attrs) {
        if (attrs == null) {
            return;
        }

        mTextSize = AttrUtils.getDimensionFromAttr(attrs, WHEELPICKER_ITEMTEXTSIZE,
                Utils.sp2px(context, WheelItemTextSize));
        mTextColor = AttrUtils.getColorFromAttr(attrs, WHEELPICKER_ITEMTEXTCOLOR,
                Color.BLACK.getValue());
        mIsTextGradual = AttrUtils.getBooleanFromAttr(attrs, WHEELPICKER_TEXTGRADUAL, true);
        mIsCyclic = AttrUtils.getBooleanFromAttr(attrs, WHEELPICKER_WHEELCYCLIC, false);
        mHalfVisibleItemCount = AttrUtils.getIntFromAttr(attrs, WHEELPICKER_HALFVISIBLEITEMCOUNT, 2);
        mItemMaximumWidthText = AttrUtils.getStringFromAttr(attrs, WHEELPICKER_ITEMMAXIMUMWIDTHTEXT, null);
        mSelectedItemTextColor = AttrUtils.getColorFromAttr(attrs, WHEELPICKER_SELECTEDTEXTCOLOR, Color.getIntColor("#ff33aaff"));
        mSelectedItemTextSize = AttrUtils.getDimensionFromAttr(attrs, WHEELPICKER_SELECTEDTEXTSIZE,
                Utils.sp2px(context, WheelSelectedItemTextSize));
        mCurrentPosition = AttrUtils.getIntFromAttr(attrs, WHEELPICKER_CURRENTITEMPOSITION, 0);
        mItemWidthSpace = AttrUtils.getDimensionFromAttr(attrs, WHEELPICKER_ITEMWIDTHSPACE,
                Utils.sp2px(context, WheelItemWidthSpace));
        mItemHeightSpace = AttrUtils.getDimensionFromAttr(attrs, WHEELPICKER_ITEMHEIGHTSPACE,
                Utils.sp2px(context, WheelItemHeightSpace));
        mIsZoomInSelectedItem = AttrUtils.getBooleanFromAttr(attrs, WHEELPICKER_ZOOMINSELECTEDITEM, true);
        mIsShowCurtain = AttrUtils.getBooleanFromAttr(attrs, WHEELPICKER_WHEELCURTAIN, true);
        mCurtainColor = AttrUtils.getColorFromAttr(attrs, WHEELPICKER_WHEELCURTAINCOLOR,
                Color.getIntColor("#303d3d3d"));
        mIsShowCurtainBorder = AttrUtils.getBooleanFromAttr(attrs, WHEELPICKER_WHEELCURTAINBORDER, true);
        mCurtainBorderColor = AttrUtils.getColorFromAttr(attrs, WHEELPICKER_WHEELCURTAINBORDERCOLOR, Color.BLACK.getValue());
        mIndicatorText = AttrUtils.getStringFromAttr(attrs, WHEELPICKER_INDICATORTEXT, null);
        mIndicatorTextColor = AttrUtils.getColorFromAttr(attrs, WHEELPICKER_INDICATORTEXTCOLOR, mSelectedItemTextColor);
        mIndicatorTextSize = AttrUtils.getDimensionFromAttr(attrs, WHEELPICKER_INDICATORTEXTSIZE, mTextSize);
    }

    /**
     * Compute text size
     */
    public void computeTextSize() {
        mTextMaxWidth = mTextMaxHeight = 0;
        if (mDataList.size() == 0) {
            return;
        }

        // 这里使用最大的,防止文字大小超过布局大小。
        mPaint.setTextSize(mSelectedItemTextSize > mTextSize ? mSelectedItemTextSize : mTextSize);

        if (mItemMaximumWidthText != null) {
            mTextMaxWidth = (int) mPaint.measureText(mItemMaximumWidthText);
        } else {
            mTextMaxWidth = (int) mPaint.measureText(mDataList.get(0).toString());
        }
        Paint.FontMetrics metrics = mPaint.getFontMetrics();
        mTextMaxHeight = (int) (metrics.bottom - metrics.top);
    }

    /**
     * Init paint
     */
    private void initPaint() {
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setDither(true);

        mPaint.setStyle(Paint.Style.FILL_STYLE);
        mPaint.setTextAlign(TextAlignment.CENTER);
        mTextPaint = new Paint();
        mTextPaint.setAntiAlias(true);
        mTextPaint.setDither(true);
        mTextPaint.setStyle(Paint.Style.FILL_STYLE);
        mTextPaint.setTextAlign(TextAlignment.CENTER);
        mTextPaint.setColor(new Color(mTextColor));
        mTextPaint.setTextSize(mTextSize);
        mSelectedItemPaint = new Paint();
        mSelectedItemPaint.setAntiAlias(true);
        mSelectedItemPaint.setDither(true);
        mSelectedItemPaint.setStyle(Paint.Style.FILL_STYLE);
        mSelectedItemPaint.setTextAlign(TextAlignment.CENTER);
        mSelectedItemPaint.setColor(new Color(mSelectedItemTextColor));
        mSelectedItemPaint.setTextSize(mSelectedItemTextSize);
        mIndicatorPaint = new Paint();
        mIndicatorPaint.setAntiAlias(true);
        mIndicatorPaint.setDither(true);
        mIndicatorPaint.setStyle(Paint.Style.FILL_STYLE);
        mIndicatorPaint.setTextAlign(TextAlignment.LEFT);
        mIndicatorPaint.setColor(new Color(mIndicatorTextColor));
        mIndicatorPaint.setTextSize(mIndicatorTextSize);
    }

    /**
     * 计算Fling极限
     * 如果为Cyclic模式则为Integer的极限值，如果正常模式，则为一整个数据集的上下限。
     */
    private void computeFlingLimitY() {
        mMinFlingY = mIsCyclic ? Integer.MIN_VALUE :
                -mItemHeight * (mDataList.size() - 1);
        mMaxFlingY = mIsCyclic ? Integer.MAX_VALUE : 0;
    }


    /**
     * 修正坐标值，让其回到dateList的范围内
     *
     * @param position 修正前的值
     * @return 修正后的值 int
     */
    private int fixItemPosition(int position) {
        if (position < 0) {
            // 将数据集限定在0 ~ mDataList.size()-1之间
            position = mDataList.size() + (position % mDataList.size());
        }
        if (position >= mDataList.size()) {
            // 将数据集限定在0 ~ mDataList.size()-1之间
            position = position % mDataList.size();
        }
        return position;
    }

    /**
     * On touch event boolean
     *
     * @param component  component
     * @param touchEvent touch event
     * @return the boolean
     */
    private boolean onTouchEvent(Component component, TouchEvent touchEvent) {
        if (mTracker == null) {
            mTracker = VelocityDetector.obtainInstance();
        }
        mTracker.addEvent(touchEvent);
        switch (touchEvent.getAction()) {
            case TouchEvent.PRIMARY_POINT_DOWN:
                if (!mScroller.isFinished()) {
                    mScroller.abortAnimation();
                    mIsAbortScroller = true;
                } else {
                    mIsAbortScroller = false;
                }
                mTracker.clear();
                mTouchDownY = mLastDownY = (int) touchEvent.getPointerScreenPosition(0).getY();
                mTouchSlopFlag = true;
                break;
            case TouchEvent.POINT_MOVE:
                if (mTouchSlopFlag && Math.abs(mTouchDownY - touchEvent.getPointerScreenPosition(0).getY()) < mTouchSlop) {
                    break;
                }
                mTouchSlopFlag = false;
                float move = touchEvent.getPointerScreenPosition(0).getY() - mLastDownY;
                mScrollOffsetY += move;
                mLastDownY = (int) touchEvent.getPointerScreenPosition(0).getY();
                invalidate();
                break;
            case TouchEvent.PRIMARY_POINT_UP:
                analyzeActionUp(touchEvent);
                mHandler.postTask(mScrollerRunnable);
                mTracker.clear();
                mTracker = null;
                break;
        }
        return true;
    }

    private void analyzeActionUp(TouchEvent touchEvent) {
        if (!mIsAbortScroller && mTouchDownY == mLastDownY) {
            simulateClick();
            if (touchEvent.getPointerScreenPosition(0).getY() > mSelectedItemRect.bottom) {
                int scrollItem = (int) (touchEvent.getPointerScreenPosition(0).getY() - mSelectedItemRect.bottom) / mItemHeight + 1;
                mScroller.startScroll(0, mScrollOffsetY, 0,
                        -scrollItem * mItemHeight);

            } else if (touchEvent.getPointerScreenPosition(0).getY() < mSelectedItemRect.top) {
                int scrollItem = (int) (mSelectedItemRect.top - touchEvent.getPointerScreenPosition(0).getY()) / mItemHeight + 1;
                mScroller.startScroll(0, mScrollOffsetY, 0,
                        scrollItem * mItemHeight);
            }
        } else {
            mTracker.calculateCurrentVelocity(1000, mMaximumVelocity, mMaximumVelocity);
            int velocity = (int) mTracker.getVerticalVelocity();
            if (Math.abs(velocity) > mMinimumVelocity) {
                mScroller.fling(0, mScrollOffsetY, 0, velocity,
                        0, 0, mMinFlingY, mMaxFlingY);
                mScroller.setFinalY(mScroller.getFinalY() +
                        computeDistanceToEndPoint(mScroller.getFinalY() % mItemHeight));
            } else {
                mScroller.startScroll(0, mScrollOffsetY, 0,
                        computeDistanceToEndPoint(mScrollOffsetY % mItemHeight));
            }
        }
        if (!mIsCyclic) {
            if (mScroller.getFinalY() > mMaxFlingY) {
                mScroller.setFinalY(mMaxFlingY);
            } else if (mScroller.getFinalY() < mMinFlingY) {
                mScroller.setFinalY(mMinFlingY);
            }
        }
    }

    /**
     * On draw *
     *
     * @param component component
     * @param canvas    canvas
     */
    private void onDraw(Component component, Canvas canvas) {
        if (!mIsOnRefreshed) {
            onRefreshed(component);
            mIsOnRefreshed = true;
        }
        mPaint.setTextAlign(TextAlignment.CENTER);
        if (mIsShowCurtain) {
            mPaint.setStyle(Paint.Style.FILL_STYLE);
            mPaint.setColor(new Color(mCurtainColor));
            canvas.drawRect(mSelectedItemRect, mPaint);
        }
        drawRect(canvas);
        int drawnSelectedPos;
        if (mItemHeight == 0) {
            drawnSelectedPos = 0;
        } else {
            drawnSelectedPos = -mScrollOffsetY / mItemHeight;
        }
        mPaint.setStyle(Paint.Style.FILL_STYLE);
        // 首尾各多绘制一个用于缓冲
        for (int drawDataPos = drawnSelectedPos - mHalfVisibleItemCount - 1;
                drawDataPos <= drawnSelectedPos + mHalfVisibleItemCount + 1; drawDataPos++) {
            int position = drawDataPos;
            if (mIsCyclic) {
                position = fixItemPosition(position);
            } else {
                if (position < 0 || position > mDataList.size() - 1) {
                    continue;
                }
            }

            T data = mDataList.get(position);
            int itemDrawY = mFirstItemDrawY + (drawDataPos + mHalfVisibleItemCount) * mItemHeight + mScrollOffsetY;
            // 距离中心的Y轴距离
            int distanceY = Math.abs(mCenterItemDrawnY - itemDrawY);

            setPaintColor(itemDrawY, distanceY);

            // 开启此选项,会将越靠近中心的Item字体放大
            if (mIsZoomInSelectedItem) {
                if (distanceY < mItemHeight) {
                    float addedSize = (mItemHeight - distanceY) / (float) mItemHeight * (mSelectedItemTextSize - mTextSize);
                    mSelectedItemPaint.setTextSize((int) (mTextSize + addedSize));
                    mTextPaint.setTextSize((int) (mTextSize + addedSize));
                } else {
                    mSelectedItemPaint.setTextSize(mTextSize);
                    mTextPaint.setTextSize(mTextSize);
                }
            } else {
                mSelectedItemPaint.setTextSize(mTextSize);
                mTextPaint.setTextSize(mTextSize);
            }
            String drawText = mDataFormat == null ? data.toString() : mDataFormat.format(data);
            // 在中间位置的Item作为被选中的。
            if (distanceY < mItemHeight / 2) {
                canvas.drawText(mSelectedItemPaint, drawText, mFirstItemDrawX, itemDrawY);
            } else {
                canvas.drawText(mTextPaint, drawText, mFirstItemDrawX, itemDrawY);
            }
        }
        if (mIndicatorText != null) {
            canvas.drawText(mIndicatorPaint, mIndicatorText, mFirstItemDrawX + mTextMaxWidth / 2, mCenterItemDrawnY);
        }
    }

    private void drawRect(Canvas canvas) {
        if (mIsShowCurtainBorder) {
            mPaint.setStyle(Paint.Style.STROKE_STYLE);
            mPaint.setColor(new Color(mCurtainBorderColor));
            canvas.drawRect(mSelectedItemRect, mPaint);
            canvas.drawRect(mDrawnRect, mPaint);
        }
    }

    private void setPaintColor(int itemDrawY, int distanceY) {
        if (mIsTextGradual) {
            // 文字颜色渐变要在设置透明度上边，否则会被覆盖
            // 计算文字颜色渐变
            if (distanceY < mItemHeight) {  // 距离中心的高度小于一个ItemHeight才会开启渐变
                float colorRatio = 1 - (distanceY / (float) mItemHeight);
                mSelectedItemPaint.setColor(mLinearGradient.getColor(colorRatio));
                mTextPaint.setColor(mLinearGradient.getColor(colorRatio));
            } else {
                mSelectedItemPaint.setColor(new Color(mSelectedItemTextColor));
                mTextPaint.setColor(new Color(mTextColor));
            }
            // 计算透明度渐变
            float alphaRatio;
            if (itemDrawY > mCenterItemDrawnY) {
                alphaRatio = (mDrawnRect.getHeight() - itemDrawY) /
                        (float) (mDrawnRect.getHeight() - (mCenterItemDrawnY));
            } else {
                alphaRatio = itemDrawY / (float) mCenterItemDrawnY;
            }

            alphaRatio = alphaRatio < 0 ? 0 : alphaRatio;
            mSelectedItemPaint.setAlpha(alphaRatio);
            mTextPaint.setAlpha(alphaRatio);
        }
    }

    /**
     * On refreshed *
     *
     * @param component component
     */
    private void onRefreshed(Component component) {
        mDrawnRect = new RectFloat(getPaddingLeft(), getPaddingTop(),
                getWidth() - getPaddingRight(), getHeight() - getPaddingBottom());
        mItemHeight = (int) (mDrawnRect.getHeight() / getVisibleItemCount());
        mFirstItemDrawX = (int) mDrawnRect.getHorizontalCenter();
        mFirstItemDrawY = (int) ((mItemHeight - (mSelectedItemPaint.ascent() + mSelectedItemPaint.descent())) / 2);
        // 中间的Item边框
        mSelectedItemRect = new RectFloat(getPaddingLeft(), mItemHeight * mHalfVisibleItemCount,
                getWidth() - getPaddingRight(), mItemHeight + mItemHeight * mHalfVisibleItemCount);
        computeFlingLimitY();
        mCenterItemDrawnY = mFirstItemDrawY + mItemHeight * mHalfVisibleItemCount;
        mScrollOffsetY = -mItemHeight * mCurrentPosition;
    }

    /**
     * Simulate click boolean
     *
     * @return the boolean
     */
    @Override
    public boolean simulateClick() {
        return super.simulateClick();
    }

    /**
     * Compute distance to end point int
     *
     * @param remainder remainder
     * @return the int
     */
    private int computeDistanceToEndPoint(int remainder) {
        if (Math.abs(remainder) > mItemHeight / 2) {
            if (mScrollOffsetY < 0) {
                return -mItemHeight - remainder;
            } else {
                return mItemHeight - remainder;
            }
        } else {
            return -remainder;
        }
    }


    /**
     * Set on wheel change listener *
     *
     * @param onWheelChangeListener on wheel change listener
     */
    public void setOnWheelChangeListener(OnWheelChangeListener<T> onWheelChangeListener) {
        mOnWheelChangeListener = onWheelChangeListener;
    }

    /**
     * Get text paint paint
     *
     * @return the paint
     */
    public Paint getTextPaint() {
        return mTextPaint;
    }

    /**
     * Get selected item paint paint
     *
     * @return the paint
     */
    public Paint getSelectedItemPaint() {
        return mSelectedItemPaint;
    }

    /**
     * Get paint paint
     *
     * @return the paint
     */
    public Paint getPaint() {
        return mPaint;
    }

    /**
     * Get indicator paint paint
     *
     * @return the paint
     */
    public Paint getIndicatorPaint() {
        return mIndicatorPaint;
    }

    /**
     * Get data list list
     *
     * @return the list
     */
    public List<T> getDataList() {
        return mDataList;
    }

    /**
     * Set data list *
     *
     * @param dataList data list
     */
    public void setDataList(List<T> dataList) {
        mDataList = dataList;
        if (dataList.size() == 0) {
            return;
        }
        computeTextSize();
        computeFlingLimitY();

        invalidate();
    }

    /**
     * Get text color int
     *
     * @return the int
     */
    public int getTextColor() {
        return mTextColor;
    }

    /**
     * 一般列表的文本颜色
     *
     * @param textColor 文本颜色
     */
    public void setTextColor(int textColor) {
        if (mTextColor == textColor) {
            return;
        }
        mTextPaint.setColor(new Color(textColor));
        mTextColor = textColor;
        mLinearGradient.setStartColor(textColor);
        invalidate();
    }

    /**
     * Get text size int
     *
     * @return the int
     */
    public int getTextSize() {
        return mTextSize;
    }

    /**
     * 一般列表的文本大小
     *
     * @param textSize 文字大小
     */
    public void setTextSize(int textSize) {
        if (mTextSize == textSize) {
            return;
        }
        mTextSize = textSize;
        mTextPaint.setTextSize(textSize);
        computeTextSize();
        invalidate();
    }

    /**
     * Get selected item text color int
     *
     * @return the int
     */
    public int getSelectedItemTextColor() {
        return mSelectedItemTextColor;
    }

    /**
     * 设置被选中时候的文本颜色
     *
     * @param selectedItemTextColor 文本颜色
     */
    public void setSelectedItemTextColor(int selectedItemTextColor) {
        if (mSelectedItemTextColor == selectedItemTextColor) {
            return;
        }
        mSelectedItemPaint.setColor(new Color(selectedItemTextColor));
        mSelectedItemTextColor = selectedItemTextColor;
        mLinearGradient.setEndColor(selectedItemTextColor);
        invalidate();
    }

    /**
     * Get selected item text size int
     *
     * @return the int
     */
    public int getSelectedItemTextSize() {
        return mSelectedItemTextSize;
    }

    /**
     * 设置被选中时候的文本大小
     *
     * @param selectedItemTextSize 文字大小
     */
    public void setSelectedItemTextSize(int selectedItemTextSize) {
        if (mSelectedItemTextSize == selectedItemTextSize) {
            return;
        }
        mSelectedItemPaint.setTextSize(selectedItemTextSize);
        mSelectedItemTextSize = selectedItemTextSize;
        computeTextSize();
        invalidate();
    }


    /**
     * Get item maximum width text string
     *
     * @return the string
     */
    public String getItemMaximumWidthText() {
        return mItemMaximumWidthText;
    }

    /**
     * 设置输入的一段文字，用来测量 mTextMaxWidth
     *
     * @param itemMaximumWidthText 文本内容
     */
    public void setItemMaximumWidthText(String itemMaximumWidthText) {
        mItemMaximumWidthText = itemMaximumWidthText;

        invalidate();
    }

    /**
     * Get half visible item count int
     *
     * @return the int
     */
    public int getHalfVisibleItemCount() {
        return mHalfVisibleItemCount;
    }

    /**
     * 设置显示数据量的个数的一半。
     * 为保证总显示个数为奇数,这里将总数拆分，总数为 mHalfVisibleItemCount * 2 + 1
     *
     * @param halfVisibleItemCount 总数量的一半
     */
    public void setHalfVisibleItemCount(int halfVisibleItemCount) {
        if (mHalfVisibleItemCount == halfVisibleItemCount) {
            return;
        }
        mHalfVisibleItemCount = halfVisibleItemCount;
    }

    /**
     * 显示的个数等于上下两边Item的个数+ 中间的Item
     *
     * @return 总显示的数量 int
     */
    public int getVisibleItemCount() {
        return mHalfVisibleItemCount * 2 + 1;
    }

    /**
     * Get item width space int
     *
     * @return the int
     */
    public int getItemWidthSpace() {
        return mItemWidthSpace;
    }

    /**
     * Set item width space *
     *
     * @param itemWidthSpace item width space
     */
    public void setItemWidthSpace(int itemWidthSpace) {
        if (mItemWidthSpace == itemWidthSpace) {
            return;
        }
        mItemWidthSpace = itemWidthSpace;
    }

    /**
     * Get item height space int
     *
     * @return the int
     */
    public int getItemHeightSpace() {
        return mItemHeightSpace;
    }

    /**
     * 设置两个Item之间的间隔
     *
     * @param itemHeightSpace 间隔值
     */
    public void setItemHeightSpace(int itemHeightSpace) {
        if (mItemHeightSpace == itemHeightSpace) {
            return;
        }
        mItemHeightSpace = itemHeightSpace;
    }

    /**
     * Get current position int
     *
     * @return the int
     */
    public int getCurrentPosition() {
        return mCurrentPosition;
    }

    /**
     * 设置当前选中的列表项,将滚动到所选位置
     *
     * @param currentPosition 设置的当前位置
     */
    public void setCurrentPosition(int currentPosition) {
        setCurrentPosition(currentPosition, true);
    }

    /**
     * 设置当前选中的列表位置
     *
     * @param currentPosition 设置的当前位置
     * @param smoothScroll    是否平滑滚动
     */
    public synchronized void setCurrentPosition(int currentPosition, boolean smoothScroll) {
        if (currentPosition > mDataList.size() - 1) {
            currentPosition = mDataList.size() - 1;
        }
        if (currentPosition < 0) {
            currentPosition = 0;
        }
        if (mCurrentPosition == currentPosition) {
            return;
        }
        if (!mScroller.isFinished()) {
            mScroller.abortAnimation();
        }

        // 如果mItemHeight=0代表还没有绘制完成，这时平滑滚动没有意义
        if (smoothScroll && mItemHeight > 0) {
            mScroller.startScroll(0, mScrollOffsetY, 0, (mCurrentPosition - currentPosition) * mItemHeight);
            int finalY = -currentPosition * mItemHeight;
            mScroller.setFinalY(finalY);
            mHandler.postTask(mScrollerRunnable);
        } else {
            mCurrentPosition = currentPosition;
            mScrollOffsetY = -mItemHeight * mCurrentPosition;
            invalidate();
            if (mOnWheelChangeListener != null) {
                mOnWheelChangeListener.onWheelSelected(mDataList.get(currentPosition), currentPosition);
            }
        }
    }

    /**
     * Is zoom in selected item boolean
     *
     * @return the boolean
     */
    public boolean isZoomInSelectedItem() {
        return mIsZoomInSelectedItem;
    }

    /**
     * Set zoom in selected item *
     *
     * @param zoomInSelectedItem zoom in selected item
     */
    public void setZoomInSelectedItem(boolean zoomInSelectedItem) {
        if (mIsZoomInSelectedItem == zoomInSelectedItem) {
            return;
        }
        mIsZoomInSelectedItem = zoomInSelectedItem;
        invalidate();
    }

    /**
     * Is cyclic boolean
     *
     * @return the boolean
     */
    public boolean isCyclic() {
        return mIsCyclic;
    }

    /**
     * 设置是否循环滚动。
     *
     * @param cyclic 上下边界是否相邻
     */
    public void setCyclic(boolean cyclic) {
        if (mIsCyclic == cyclic) {
            return;
        }
        mIsCyclic = cyclic;
        computeFlingLimitY();
    }

    /**
     * Get minimum velocity int
     *
     * @return the int
     */
    public int getMinimumVelocity() {
        return mMinimumVelocity;
    }

    /**
     * 设置最小滚动速度,如果实际速度小于此速度，将不会触发滚动。
     *
     * @param minimumVelocity 最小速度
     */
    public void setMinimumVelocity(int minimumVelocity) {
        mMinimumVelocity = minimumVelocity;
    }

    /**
     * Get maximum velocity int
     *
     * @return the int
     */
    public int getMaximumVelocity() {
        return mMaximumVelocity;
    }

    /**
     * 设置最大滚动的速度,实际滚动速度的上限
     *
     * @param maximumVelocity 最大滚动速度
     */
    public void setMaximumVelocity(int maximumVelocity) {
        mMaximumVelocity = maximumVelocity;
    }

    /**
     * Is text gradual boolean
     *
     * @return the boolean
     */
    public boolean isTextGradual() {
        return mIsTextGradual;
    }

    /**
     * 设置文字渐变，离中心越远越淡。
     *
     * @param textGradual 是否渐变
     */
    public void setTextGradual(boolean textGradual) {
        if (mIsTextGradual == textGradual) {
            return;
        }
        mIsTextGradual = textGradual;
        invalidate();
    }

    /**
     * Is show curtain boolean
     *
     * @return the boolean
     */
    public boolean isShowCurtain() {
        return mIsShowCurtain;
    }

    /**
     * 设置中心Item是否有幕布遮盖
     *
     * @param showCurtain 是否有幕布
     */
    public void setShowCurtain(boolean showCurtain) {
        if (mIsShowCurtain == showCurtain) {
            return;
        }
        mIsShowCurtain = showCurtain;
        invalidate();
    }

    /**
     * Get curtain color int
     *
     * @return the int
     */
    public int getCurtainColor() {
        return mCurtainColor;
    }

    /**
     * 设置幕布颜色
     *
     * @param curtainColor 幕布颜色
     */
    public void setCurtainColor(int curtainColor) {
        if (mCurtainColor == curtainColor) {
            return;
        }
        mCurtainColor = curtainColor;
        invalidate();
    }

    /**
     * Is show curtain border boolean
     *
     * @return the boolean
     */
    public boolean isShowCurtainBorder() {
        return mIsShowCurtainBorder;
    }

    /**
     * 设置幕布是否显示边框
     *
     * @param showCurtainBorder 是否有幕布边框
     */
    public void setShowCurtainBorder(boolean showCurtainBorder) {
        if (mIsShowCurtainBorder == showCurtainBorder) {
            return;
        }
        mIsShowCurtainBorder = showCurtainBorder;
        invalidate();
    }

    /**
     * Get curtain border color int
     *
     * @return the int
     */
    public int getCurtainBorderColor() {
        return mCurtainBorderColor;
    }

    /**
     * 幕布边框的颜色
     *
     * @param curtainBorderColor 幕布边框颜色
     */
    public void setCurtainBorderColor(int curtainBorderColor) {
        if (mCurtainBorderColor == curtainBorderColor) {
            return;
        }
        mCurtainBorderColor = curtainBorderColor;
        invalidate();
    }

    /**
     * Set indicator text *
     *
     * @param indicatorText indicator text
     */
    public void setIndicatorText(String indicatorText) {
        mIndicatorText = indicatorText;
        invalidate();
    }

    /**
     * Set indicator text color *
     *
     * @param indicatorTextColor indicator text color
     */
    public void setIndicatorTextColor(int indicatorTextColor) {
        mIndicatorTextColor = indicatorTextColor;
        mIndicatorPaint.setColor(new Color(mIndicatorTextColor));
        invalidate();
    }

    /**
     * Set indicator text size *
     *
     * @param indicatorTextSize indicator text size
     */
    public void setIndicatorTextSize(int indicatorTextSize) {
        mIndicatorTextSize = indicatorTextSize;
        mIndicatorPaint.setTextSize(mIndicatorTextSize);
        invalidate();
    }

    /**
     * Get data format format
     *
     * @return the format
     */
    public Format getDataFormat() {
        return mDataFormat;
    }

    /**
     * 设置数据集格式
     *
     * @param dataFormat 格式
     */
    public void setDataFormat(Format dataFormat) {
        mDataFormat = dataFormat;
        invalidate();
    }

    /**
     * On wheel change listener
     *
     * @param <T> parameter
     */
    public interface OnWheelChangeListener<T> {
        /**
         * On wheel selected *
         *
         * @param item     item
         * @param position position
         */
        void onWheelSelected(T item, int position);
    }
}
